package com.runas.util;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.*;
import java.text.DecimalFormat;
import java.util.*;

public class SemanticBlockUtil {

	public static class SingleParsedBlocks<T> {
		protected List<List<T>> rows;

		protected Map allowBlank;

		protected List<List<T>> arrangedBlocks;
		
		private Class<T> clazz;
		
		public SingleParsedBlocks() {
		}
		
		public SingleParsedBlocks(List<List<T>> rows, Map allowBlank) {
			this.rows = rows;
			this.allowBlank = allowBlank == null ? Collections.EMPTY_MAP : allowBlank;
		}

		public boolean isAllowBlank(int row) {
			return allowBlank.containsKey(row);
		}

		public boolean isEmpty() {
			return rows == null;
		}

		public List<List<T>> getRows() {
			return rows;
		}

		byte c = 0;

		private boolean cacheArrangedBlocks = false;

		public boolean isCacheArrangedBlocks() {
			return cacheArrangedBlocks;
		}

		public void setCacheArrangedBlocks(boolean cacheArrangedBlocks) {
			this.cacheArrangedBlocks = cacheArrangedBlocks;
		}

		public void arrangePlain(ArrangeCallBack<T> cb) {
			try {
				int[] path = new int[rows.size()];
				Arrays.fill(path, -1);
				if (arrangedBlocks == null) {
					if (cacheArrangedBlocks)
						arrangedBlocks = new ArrayList<List<T>>();
					arrange0(this, 0, cb, path, arrangedBlocks);
				} else {
					for (List<T> bs : arrangedBlocks) {
						cb.call(bs);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			cb.finish();
		}

		private void arrange0(SingleParsedBlocks parseBlocks, int r, ArrangeCallBack<T> cb, int[] path,
				List<List<T>> recorder) {
			if (r == rows.size()) {
				List<T> result = new ArrayList<T>(rows.size());
//				Object[] result = new Object[rows.size()];
				for (int i = 0; i < rows.size(); i++) {
					List<T> row = rows.get(i);
					T v = null;
					if (path[i] != -1 && path[i] < row.size() && (v = row.get(path[i])) != null) 
						;
					result.add(v);
				}
				if (recorder != null)
					recorder.add(result);
				cb.call(result);
			} else {
				for (int c = 0; c < rows.get(r).size(); c++) {
					path[r] = c;
					arrange0(parseBlocks, r + 1, cb, path, recorder);
					if (cb.isExit())
						return;
					path[r] = -1;
				}
				if (allowBlank.containsKey(r)) {
					arrange0(parseBlocks, r + 1, cb, path, recorder);
					if (cb.isExit())
						return;
					path[r] = -1;
				}
			}
		}

		public List<T> getRow(int row) {
			return rows.get(row);
		}

		public List<T> setRow(int row, List<T> data) {
			return rows.set(row, data);
		}
		
	}

	abstract public static class Exitable {
		private boolean _exit = false;

		public boolean isExit() {
			return _exit;
		}

		public void exit() {
			_exit = true;
		}
	}

	abstract public static class ArrangeCallBack<T> extends Exitable {
		abstract public void call(List<T> blocks);
		public void finish() {};
	}

	public static String readableFileSize(long size) {
		if (size <= 0) return "0";
		final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
		int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
		return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
	}

}
